import json
import os
import shutil
import time
import traceback
import zipfile

from flask import Blueprint, request, jsonify, render_template

from applications.common.utils.http import fail_api, success_api, table_api
from applications.common.utils.rights import authorize
from applications.models import Power
from applications.schemas.admin_power import PowerSchema
from mod.fun import generate_tree_with_parent, zipfolder, tconf

dir_path = os.path.dirname(os.path.realpath(__file__)).replace("\\", "/")

bp = Blueprint('dev_plugin', __name__, url_prefix='/plugin')

@bp.route('/')
def index():
    return render_template('plugin/index.html')

@bp.route('/data', methods=['GET'])
@authorize('dev:plugin:list')
def list():
    confjson = "etc/plugin/config.json"
    if os.path.exists(confjson):
        plugin_enabled = json.load(open('etc/plugin/config.json', 'r', encoding='utf-8'))
    else:
        plugin_enabled = {}
    actkeys = plugin_enabled.keys()

    plugins = os.listdir('plugins')
    rt = []
    for plugin in plugins:
        if plugin in ['__pycache__','dev']:
            continue
        installedkey = 'etc/plugin/'+plugin+'.lock'

        if os.path.isdir('plugins/' + plugin):
            with open('plugins/' + plugin + '/__init__.json', 'r', encoding='utf-8') as f:
                info = json.load(f)
            # 检查依赖
            info['dependon'] = 1
            if 'dependencies' in info:
                for dep in info['dependencies'].keys():
                    if dep not in plugins:
                        info['dependon'] = 0
            if os.path.exists(installedkey):
                info['installed'] = 1
                # 是否激活
                if plugin in actkeys:
                    info['enable'] = 1
                else:
                    info['enable'] = 0
            else:
                info['installed'] = 0
                info['enable'] = -1

            rt.append(info)
    return table_api(data=rt, count=len(rt))

@bp.route('/add', methods=['GET', 'POST'])
@authorize('dev:plugin:add')
def add():
    ##post
    if request.method == 'POST':
        data = request.form
        print(data)
        if data['name'] == '' or data['version'] == '' or data['folder'] == '':
            return fail_api('请填写完整信息')
        folder = data['folder']
        if os.path.isdir('plugins/' + folder) or folder in ['admin', 'plugins', 'system','dev']:
            return fail_api('文件夹存在或是不能使用的！')
        #创建文件夹
        os.mkdir('plugins/' + folder)
        os.makedirs('plugins/' + folder + '/static')
        os.makedirs('plugins/' + folder + '/templates')
        os.makedirs('plugins/' + folder + '/models')
        os.makedirs('plugins/' + folder + '/views')
        os.makedirs('plugins/' + folder + '/schemas')
        os.makedirs('plugins/' + folder + '/config')
        os.makedirs('plugins/' + folder + '/views/admin')
        os.makedirs('plugins/' + folder + '/views/index')
        os.makedirs('plugins/' + folder + '/templates/admin')
        os.makedirs('plugins/' + folder + '/templates/index')
        # 创建requrements.txt
        pip = data['pip']
        if pip:
            with open('plugins/' + folder + '/requirements.txt', 'w', encoding='utf-8') as f:
                f.write(pip)
        # 创建根 __init__.py
        data_init = '''
import os
from flask import Flask, Blueprint
from plugins.{0}.views import init_bp

dir_path = os.path.dirname(os.path.realpath(__file__)).replace("\\\\", "/")
plugin_path = dir_path[dir_path.rfind("/")+1:]

{0}_bp = Blueprint("{0}", __name__, url_prefix="/{0}", template_folder='templates',
               static_folder='static')

def event_init(app: Flask):
    init_bp({0}_bp)
    app.register_blueprint({0}_bp)

def event_enable(app: Flask):
    pass

def event_disable(app: Flask):
    pass

def event_install(app: Flask):
    if os.path.exists(dir_path + "/requirements.txt"):
        app.logger.info("正在安装依赖... for plugin " + plugin_path)
        os.system("pip install -r " + dir_path + "/requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple")

def event_remove(app: Flask):
    pass
'''.format(folder)
        with open('plugins/' + folder + '/__init__.py', 'w', encoding='utf-8') as f:
            f.write(data_init)

        # 创建__init__.json
        depends = {}
        if data['dependencies'] != '':
            try:
                depends = json.loads(data['dependencies'])
            except:
                pass
        data_init_dict = {
            "name": data['name'],
            "version": data['version'],
            "folder": data['folder'],
            "description": data['description'],
            "author": data['author'],
            "license": data['license'],
            "url": data['url'],
            "dependencies": depends,
            "power_pid": data['power'],
            "pip": data['pip']
        }
        with open('plugins/' + folder + '/__init__.json', 'w', encoding='utf-8') as f:
            json.dump(data_init_dict, f)

        # 创建views/__init__.py
        data_view_init = '''
from plugins.{0}.views.admin import bp_{0}_admin
from plugins.{0}.views.index import bp_{0}_index

def init_bp(app):
    bp_{0}_admin(app)
    bp_{0}_index(app)
'''.format(folder)
        with open('plugins/' + folder + '/views/__init__.py', 'w', encoding='utf-8') as f:
            f.write(data_view_init)

        # 创建views/admin/__init__.py
        data_view_admin_init = '''
from .index import bp as bp_index

def bp_{0}_admin(app):
    app.register_blueprint(bp_index)
'''.format(folder)
        with open('plugins/' + folder + '/views/admin/__init__.py', 'w', encoding='utf-8') as f:
            f.write(data_view_admin_init)
        # 创建views/index/__init__.py
        data_view_index_init = '''
from .index import bp as bp_index

def bp_{0}_index(app):
    app.register_blueprint(bp_index)
'''.format(folder)
        with open('plugins/' + folder + '/views/index/__init__.py', 'w', encoding='utf-8') as f:
            f.write(data_view_index_init)
        # 创建views/admin/index.py
        data_view_admin_index = '''
import os
from flask import Blueprint, request, jsonify

dir_path = os.path.dirname(os.path.realpath(__file__)).replace("\\\\", "/")

bp = Blueprint('{0}_admin', __name__, url_prefix='/admin')

@bp.route('/')
def index():
    return "this is {0} admin index page"
'''.format(folder)
        with open('plugins/' + folder + '/views/admin/index.py', 'w', encoding='utf-8') as f:
            f.write(data_view_admin_index)
        # 创建views/index/index.py
        data_view_index_index = '''
import os
from flask import Blueprint, request, jsonify
from applications.common.utils.http import fail_api, success_api, table_api

dir_path = os.path.dirname(os.path.realpath(__file__)).replace('\\\\', '/')

bp = Blueprint('{0}_index', __name__)

@bp.route("/")
def index():
    return 'this is {0} index'
'''.format(folder)
        with open('plugins/' + folder + '/views/index/index.py', 'w', encoding='utf-8') as f:
            f.write(data_view_index_index)
        # 创建config
        data_config = '''{}
'''
        with open('plugins/' + folder + '/config/config.json', 'w', encoding='utf-8') as f:
            f.write(data_config)
        return success_api(msg="初始化成功！")

    else:
        return render_template('plugin/add.html')

# 打包应用
@bp.route('/package', methods=['POST'])
def package():
    dirname = request.json.get('folder')
    newver = request.json.get('newver')
    power_pid = request.json.get('power_pid')
    if not dirname:
        return fail_api(msg="参数错误！")
    # 读取应用信息
    try:
        if os.path.exists('plugins/' + dirname + '/__init__.json'):
            with open('plugins/' + dirname + '/__init__.json', 'r', encoding='utf-8') as f:
                config = json.load(f)
            config['version'] = newver
            config['power_pid'] = power_pid
            config['package'] = 1
            with open('plugins/' + dirname + '/__init__.json', 'w', encoding='utf-8') as f:
                json.dump(config, f, ensure_ascii=False, indent=4)
        else:
            return fail_api(msg="读取配置文件失败！")
    except Exception as e:
        msg = 'str(Exception):\t' + str(Exception) + "\n"
        msg += 'str(e):\t\t' + str(e) + "\n"
        msg += 'repr(e):\t' + repr(e) + "\n"
        msg += 'traceback.format_exc():\n%s' + traceback.format_exc()
        print(msg)
        return fail_api(msg="读取配置文件失败！")
    if 'power_pid' in config:
        # 读取权限信息
        powerquery = Power.query.all()
        powerdata = {
            "data": PowerSchema(many=True).dump(powerquery)
        }
        res = generate_tree_with_parent(powerdata['data'], config['power_pid'], pid='parent_id', cache=[],child='child')
        with open('plugins/' + dirname + '/config/power.json', 'w', encoding='utf-8') as f:
            json.dump(res, f, ensure_ascii=False, indent=4)
    if not os.path.exists('data/plugins/' + dirname):
        os.makedirs('data/plugins/' + dirname)
    if not os.path.exists('data/plugins/' + dirname + '/' + newver):
        os.makedirs('data/plugins/' + dirname + '/' + newver)
    filename = 'data/plugins/' + dirname + '/' + newver + '/' + dirname +'_' + time.strftime("%Y%m%d%H%M%S", time.localtime()) + '.zip'
    zipfolder('plugins/' + dirname, filename)
    # zip_folder('plugins/' + dirname, filename)
    return success_api(msg="打包成功！")

@bp.route('upload', methods=['POST'])
def upload():
    uploadpath = 'data/pluginupload/'
    if not os.path.exists(uploadpath):
        os.makedirs(uploadpath)
    if 'file' in request.files:
        dstfile = request.files['file']
        if dstfile.filename == '':
            return fail_api(msg="请选择文件！")
        if dstfile.filename[-3:].lower() != 'zip':
            return fail_api(msg='请上传zip格式的文件！')
        dstfile.save(uploadpath + dstfile.filename)
        # print(uploadpath + dstfile.filename)
        if os.path.exists(uploadpath + dstfile.filename):
            with zipfile.ZipFile(uploadpath + dstfile.filename) as zfile:
                if '__init__.json' in zfile.namelist():
                    with zfile.open('__init__.json', 'r') as f:
                        data = json.load(f)
                    apppath = 'plugins/' + data['folder'] + '/'
                    if not os.path.exists(apppath):
                        os.makedirs(apppath, 0o777)
                    # 解压
                    for fn in zfile.namelist():
                        nfilename = fn.encode('cp437').decode('gbk')
                        # print(nfilename)
                        if '/' in nfilename:
                            tmpfile = nfilename.split('/')
                            tdir = os.path.join('/'.join(tmpfile[:-1]))
                            if not os.path.exists(apppath + tdir):
                                os.makedirs(apppath + tdir, 0o777)
                        if nfilename[-1:] == '/':
                            if not os.path.exists(apppath + nfilename):
                                os.makedirs(apppath + nfilename, 0o777)
                        else:
                            with open(apppath + nfilename, 'wb') as outfile:
                                with zfile.open(fn, 'r') as orifile:
                                    shutil.copyfileobj(orifile, outfile)
            return success_api(msg='上传成功！')
        else:
            return fail_api(msg='上传失败！')
    else:
        return fail_api(msg='请选择文件！')

@bp.route('/remove', methods=['POST'])
def remove():
    dirname = request.json.get('folder')
    installedkey = 'etc/plugin/' + dirname + '.lock'
    if os.path.exists(installedkey):
        return fail_api(msg='插件已经安装，不允许删除！')
    confjson = "etc/plugin/config.json"
    if os.path.exists(confjson):
        plugin_enabled = json.load(open('etc/plugin/config.json', 'r', encoding='utf-8'))
    else:
        plugin_enabled = {}
    actkeys = plugin_enabled.keys()
    if dirname in actkeys:
        return fail_api(msg='插件已经激活，不允许删除！')
    appdir = 'plugins/' + dirname + '/'
    # @todo 删除power权限及数据表格

    os.remove(installedkey)
    shutil.rmtree(appdir)

@bp.route('/uninstall', methods=['POST'])
def uninstall():
    dirname = request.json.get('folder')
    installedkey = 'etc/plugin/' + dirname + '.lock'
    if not os.path.exists(installedkey):
        return fail_api(msg='插件还未安装！')
    appdir = 'plugins/' + dirname + '/'
    # 取消app激活状态
    confjson = "etc/plugin/config.json"
    if os.path.exists(confjson):
        plugin_enabled = json.load(open('etc/plugin/config.json', 'r', encoding='utf-8'))
    else:
        plugin_enabled = {}
    actkeys = plugin_enabled.keys()
    if dirname in actkeys:
        plugin_enabled.pop(dirname)
        with open(confjson, 'w', encoding='utf-8') as f:
            json.dump(plugin_enabled, f, ensure_ascii=False, indent=4)

    # @todo 删除power权限及数据表格
    os.remove(installedkey)
    return success_api(msg='卸载成功！')


@bp.route('/install', methods=['POST'])
def install():
    dirname = request.json.get('folder')
    installedkey = 'etc/plugin/' + dirname + '.lock'
    if os.path.exists(installedkey):
        return fail_api(msg='插件已安装！')
    appdir = 'plugins/' + dirname + '/'
    configjson = appdir + '__init__.json'
    if not os.path.exists(configjson):
        return fail_api(msg='插件配置文件不存在！')
    with open(configjson, 'r', encoding='utf-8') as f:
        config = json.load(f)
    # 安装依赖
    if os.path.exists(appdir + "/requirements.txt"):
        os.system("pip install -r " + appdir + "/requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple")

    # @todo 初始化数据库表格
    # @todo 添加power

    with open(installedkey, 'w', encoding='utf-8') as f:
        f.write(config['version'])
    return success_api(msg='安装成功！')

@bp.route('/enable', methods=['POST'])
def enable():
    dirname = request.json.get('folder')
    installedkey = 'etc/plugin/' + dirname + '.lock'
    if not os.path.exists(installedkey):
        return fail_api(msg='插件还未安装！')
    appconf = 'plugins/' + dirname + '/__init__.json'
    if not os.path.exists(appconf):
        return fail_api(msg='插件不存在！')
    # 读取配置文件
    # appconfig = json.load(open(appconf, 'r', encoding='utf-8'))
    with open(appconf,'r', encoding='utf-8') as f:
        appconfig = json.load(f)
    confjson = "etc/plugin/config.json"
    if os.path.exists(confjson):
        plugin_enabled = json.load(open('etc/plugin/config.json', 'r', encoding='utf-8'))
    else:
        plugin_enabled = {}
    actkeys = plugin_enabled.keys()
    if dirname in actkeys:
        return fail_api(msg='插件已经激活！')
    else:
        plugin_enabled[dirname] = appconfig
        with open(confjson, 'w', encoding='utf-8') as f:
            json.dump(plugin_enabled, f, ensure_ascii=False, indent=4)
        restartkey = 'etc/conf/restart.lock'
        with open(restartkey, 'w', encoding='utf-8') as f:
            f.write(str(int(time.time())))
        return success_api(msg='激活成功！')

@bp.route('/disable', methods=['POST'])
def disable():
    dirname = request.json.get('folder')
    installedkey = 'etc/plugin/' + dirname + '.lock'
    if not os.path.exists(installedkey):
        return fail_api(msg='插件还未安装！')
    appconf = 'plugins/' + dirname + '/__init__.json'
    if not os.path.exists(appconf):
        return fail_api(msg='插件不存在！')
    # 读取配置文件
    # appconfig = json.load(open(appconf, 'r', encoding='utf-8'))
    confjson = "etc/plugin/config.json"
    if os.path.exists(confjson):
        plugin_enabled = json.load(open('etc/plugin/config.json', 'r', encoding='utf-8'))
    else:
        plugin_enabled = {}
    actkeys = plugin_enabled.keys()
    if dirname not in actkeys:
        return fail_api(msg='插件已经禁用。')
    else:
        del(plugin_enabled[dirname])
        with open(confjson, 'w', encoding='utf-8') as f:
            json.dump(plugin_enabled, f, ensure_ascii=False, indent=4)
        restartkey = 'etc/conf/restart.lock'
        with open(restartkey, 'w', encoding='utf-8') as f:
            f.write(str(int(time.time())))
        return success_api(msg='禁用成功！')

@bp.route('/sysrestart', methods=['GET', 'POST'])
def sysrestart():
    restartcmd = tconf('sys','rebootcmd')
    pid = os.getpid()
    now = int(time.time())
    restartkey = 'etc/conf/restart.lock'
    if os.path.exists(restartkey):
        with open(restartkey, 'r', encoding='utf-8') as f:
            restarttime = int(f.read())
        if now - restarttime < 300:
            if restartcmd != '':
                os.system(restartcmd)
                return success_api(msg='重启成功！')
            else:
                return fail_api(msg='重启命令为空！')
        else:
            return fail_api(msg='重启任务已超时！')
    else:
        return fail_api(msg='重启任务不存在！')